
<template>
  <el-form ref="ruleForm" v-bind="$attrs" :model="state.form">
    <div class="form-item" v-for="(row, $index) in state.form.list" :key="$index" type="flex" :gutter="gutter">
      <div class="form-item-index" v-if="showIndex">
        <div class="cell">{{ $index + 1 }}</div>
      </div>
      <el-row class="form__body-wrapper">
        <template v-for="(item) in formList">
          <el-col
            v-if="!item.hidden"
            :key="item.prop"
            :xl="item.itemSpan || itemSpan"
            :md="item.itemSpan || 12"
            :xs="24"
          >
            <el-form-item 
              :prop="`list.${$index}.${item.prop}`"
              :label="`${item.label}`"
              :rules="item.rules"
            >
           <!-- custom 自定义 -->
            <slot v-if="item.valueType === 'custom'" :name="item.prop" :model="row" :item="item"> </slot>
            <FormControls
              v-else
              :valueType="item.valueType"
              :options="item.options ?? []"
              v-bind="item.fieldProps"
              :on="item.on"
              v-model="row[item.prop]"
            />
           </el-form-item>
          </el-col>
        </template>
      </el-row>
      <div class="form-item-operation" v-if="showOperation">
        <slot name="operation" :row="row" :index="$index">
          <el-button
            size="small"
            type="primary"
            :link="true"
            @click="deleteData($index)"
          >
            删除
          </el-button>
        </slot>
      </div>
    </div>
  </el-form>
</template>

  
<script lang="ts">
export default {
  name: "DynamicForm",
  inheritAttrs: false,
};
</script>

<script setup lang="ts">
interface RowProps {
  [key: string]: any
}

interface DynamicFormProps {
  formList: any[];
  gutter?: number;
  itemSpan?: number;
  rowKey?: string;
  // 显示序号
  showIndex?: boolean;
  // 显示操作列
  showOperation?: boolean;
}

// 接受父组件参数，配置默认值
const props = withDefaults(defineProps<DynamicFormProps>(), {
  formList: () => [],
  gutter: 16,
  itemSpan: 6,
  rowKey: "id",
  showIndex: true,
  showOperation: true
});

const state = reactive<{
  form: {
    list: RowProps[]
  },
}>({
  form: {
    list: []
  }
})

const ruleForm = ref();
// 校验表单
function validate(...args: any[]) {
  return ruleForm.value.validate(...args);
}
// 重置表单
function resetFields(...args: any[]) {
  ruleForm.value.resetFields(...args);
}
// 重置表单
function clearValidate() {
  ruleForm.value.clearValidate();
}

// 获取数据
const getData = () => {
  return state.form.list
}
// 设置数据
const setData = (arr: RowProps[]) => {
  if (!arr || !arr.length) {
    state.form.list = []
    return
  }
  state.form.list = arr.map(rowItem => ({
    ...rowItem
  }))
  nextTick(() => {
    clearValidate()
  })
}

// 获取数据
const addData = (row: RowProps = {}) => {
  const formItem = props.formList
    .reduce(
      (r, c) => ({
        ...r,
        [c.prop]: c.defaultValue || ''
      }),
      {}
    )
  const obj = {
    ...formItem,
    ...row
  }
  obj[props.rowKey] = row[props.rowKey] || Date.now()
  state.form.list.push(obj)
}
// 删除数据
const deleteData = (idx: number) => {
  state.form.list.splice(idx, 1)
  ruleForm.value.fields?.forEach((n: any) => {
    if (n.prop && n.prop.split('.')[1] === idx) {
      n.clearValidate()
    }
  })
}

// 暴露给父组件的参数和方法
defineExpose({
  validate,
  resetFields,
  setData,
  getData,
  addData,
  deleteData,
});
</script>

<style lang='scss' scoped>
.form-item {
  display: flex;
}
.form-item-index {
  min-width: 28px;
  height: 32px;
  margin-bottom: 18px;
  display: flex;
  align-items: center;
  .cell {
    width: 24px;
    height: 24px;
    background: rgba(133, 144, 166, 0.1);
    border: 1px solid #e4e6e9;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 400;
    font-size: 12px;
    color: #333;
  }
}
.form__body-wrapper {
  flex: 1;
  .el-form-item {
    display: flex;

    ::v-deep {
      >.el-form-item__label {
        white-space: pre;
      }

      >.el-form-item__content {
        width: 1px;
        flex: 1;
        display: flex;
        align-items: center;
        margin-left: 0 !important;

        > {
          .el-input {
            flex: 1;
          }

          .el-select {
            flex: 1;
          }

          .el-cascader {
            flex: 1;
          }
        }
      }
    }
  }
}
.form-item-operation {
  height: 32px;
  margin-left: 10px;
  margin-bottom: 18px;
  display: flex;
  align-items: center;
}
</style>